/**
 * Intent.js
 * Auth:MingZeY 1552904342@qq.com
 * 版本：2.1
 * 时间：2021/11/13
 * 
 * Dec:跨 Tick 编程基础框架
 * 
 * 使用说明：先自己研究，我后面写文档
 * 最简单的使用方式：3个步骤
 * 1.new 一个IntentStack
 * 2.给这intentStack设置setControlObj一般是creep
 * 3.设置exceptionHandler
 * 4.写intent，intent构造 [controlObj的方法，参数]，一些特殊的intent构造可以操作intent栈，怎么用看代码
 * 5.loop里面调用intentStack.next();
 * 
 * 相比较20年的intent,现在的Intent虽然使用性上比较复杂，但具备高拓展性，建议自己封装之后再使用
 * 另外，这个版本的没有序列化方法，自己封装后实现
 */


//常量
Object.assign(global, {

    //意外处理可用状态值
    INTENT_FINISHED:"INTENT_FINISHED",//表示整个 Intent 链已经结束，可以在这里重新调用
    INTENT_NO_CONTROL_OBJ:"INTENT_NO_CONTROL_OBJ",//表示 IntentStack 对象没有找到操作对象
    INTENT_NO_CONTROL_OBJ_FUNCTION:"INTENT_NO_CONTROL_OBJ_FUNCTION",//表示 操作对象 不具有某个方法
    INTENT_CANT_FIND_OBJ:"INTENT_CANT_FIND_OBJ",//表示之前 操作对象 存在，但因某种原因无法找到，例如Creep死亡

    //意外处理可用返回值
    INTENT_RETURN_DEFAULT:"INTENT_RETURN_DEFAULT",//默认情况 下一个tick继续执行
    INTENT_RETURN_RELOAD:"INTENT_RETURN_RELOAD",//重载 在当前 tick 立即 进行 next()操作;
    INTENT_RETURN_FAIL:"INTENT_RETURN_FAIL",//失败 整个 IntentStack 重置


    //控制intent栈操作方法
    INTENT_STOP:"INTENT_STOP",//插入暂停 intent构造 [INTENT_STOP,tick]
    INTENT_IN:"INTENT_IN",//进入节点 intent构造 [INTENT_IN,一个栈名]
    INTENT_OUT:"INTENT_OUT",//退出节点 intent构造 [INTENT_OUT]
    INTENT_SYNC:"INTENT_SYNC",//同步执行 intent构造 [INTENT_SYNC,同步数量]
    INTENT_THROW:"INTENT_THROW",//抛出异常 intent构造 [INTENT_THROW,自定义异常名称]
    INTENT_PACK:"INTENT_PACK",//添加栈包 intent构造 [INTENT_PACK,栈包名称]
    INTENT_CHECK:"INTENT_CHECK",//进入检查 intent构造 [INTENT_CHECK,data]
})



let IntentObject = class IntentObject{
    constructor(obj){
        if(obj != undefined){
            if(obj.constructor.name == "String"){
                return {
                    type:"IntentObject",
                    id:obj
                }
            }else if(obj.id != undefined){
                return {
                    type:"IntentObject",
                    id:obj.id,
                }
            }
        }
        throw TypeError("[IntentObject] 需要一个 Screeps 类具有 id 属性的对象 或 一个id字符串");
    }
}

let Exception = class Exception{
    constructor(applyObj,intent,code,trace){
        this.applyObj = applyObj;
        this.intent = intent;
        this.code = code;
        this.trace = trace;

        //帮助消息
        switch(code){
            case INTENT_FINISHED:
                this.msg = "这表示当前的Intent栈已经执行完毕，您需要使用add或push来增加新的intent";
                break;
            case INTENT_NO_CONTROL_OBJ:
                this.msg = "这表示没有设置Intent控制的对象，使用setControlObject来设置，一般是Creep对象";
        }
    }

    printStackTrace(customMsg){
        let msg = `${this.applyObj.constructor.name} Msg : ${customMsg} \nat ${JSON.stringify(this.intent)}\n${this.trace}</span>`;
        if(customMsg != undefined){
            msg = `<span style="color:yellow">[Intent] Exception: ${this.code}\n`+msg;
        }else{
            msg = `<span style="color:yellow">[Intent] Exception: ${this.code}\n` + msg;
        }
        console.log(msg);
    }
}

let IntentStack = class IntentStack{


    constructor(){
        this.stack = []; //保存的intent栈
        this.handlerStack = [];//异常处理器栈，同时可以提取状态栈(状态栈是TickStream中的概念，Intent里面Handler名称其实就是一个状态)

        this.applyObj = undefined;//异常处理器的this指向，可被重新设置
        this.isOnException = false;//标记是否处于异常处理状态

        this.intentPackFinder = undefined;//栈包定位器

        this.exceptionTempStack = [];//异常处理状态下的临时 intent栈
        // this.exceptionHandler = function(){};//可以被设置，作为默认的处理器，这样就不需要使用exceptionStackHandlerFinder
        this.exceptionStackHandler = undefined;//如果使用了INTENT_IN or INTENT_OUT 必须设置下面异常处理器定位器
        this.exceptionStackHandlerFinder = undefined;//设置异常处理器的定位器，如需实现多层intent栈不同处理器必须使用它

        this.checkHandler = function(){};
        this.checkStackHandler = undefined;
        this.checkStackHandlerFinder = undefined;

        //调试配置
        this.nextCallStackDeep = 0;//next调用栈深度，intent运行的一个指标，可以理解为代码(Intent栈)复杂度
    }

    //** Public */
    setControlObj(obj){//设置控制对象
        this.controlObj = obj;
    }

    //** Public */
    setApplyObj(obj){//设置异常处理器的this指向，默认为本身，可以用此对IntentStack拓展
        this.applyObj = obj;
    }

    //** Public */
    setExceptionHandler(fn){//设置默认的异常处理器

        if(fn == undefined){
            throw TypeError("[INTENT] 需要一个 function(exception) 类型的数据作为异常处理器");
        }

        if(fn.constructor.name != "Function"){
            throw TypeError("[INTENT] 需要一个 function(exception) 类型的数据作为异常处理器");
        }

        this.exceptionHandler = fn;
    }

    //** Public */
    setExceptionStackHandlerFinder(fn){//设置异常处理器的定位器，如需实现多层intent不同处理器必须使用它
        if(fn.constructor.name != "Function"){
            throw TypeError("[INTENT] 需要一个 function 类型的数据作为异常处理器的定位器");
        }
        this.exceptionStackHandlerFinder = fn;
    }

    //** Public */
    setCheckHandler(fn){
        if(fn.constructor.name != "Function"){
            throw TypeError("[INTENT] 需要一个 function 类型的数据作为检查处理器");
        }

        this.checkHandler = fn;
    }

    //** Public */
    setCheckStackHandlerFinder(fn){
        if(fn.constructor.name != "Function"){
            throw TypeError("[Intent] 需要一个 function 类型的数据作为检查处理器的定位器");
        }
        this.checkStackHandlerFinder = fn;
    }

    //** Public */
    setIntentPackFinder(fn){//设置栈包定位器
        if(fn.constructor.name != "Function"){
            throw TypeError("[Intent] 需要一个 function 类型的数据作为 IntentPackFinder");
        }
        this.intentPackFinder = fn;
    }

    //** Public */
    add(funName,...data){//添加 intent funName，对于controlObj存在的方法;data这个方法的入参数据     
        //数据检查
        if(funName == undefined){
            throw TypeError("[Intent] 函数名称不能为 undefined，请查看add的入参方法");
        }

        if(data == undefined){//处理数据
            data = [];
        };data.unshift(funName);//组合funName和数据data

        // for(let i of data){//数据检查
        //     if(!(i.constructor.name == "String" 
        //     || i.constructor.name == "Number"
        //     || i.constructor.name == "Array"
        //     || (i.constructor.name == "Object" && i.type == "IntentObject"))){
        //         throw TypeError("[Intent] Intent 栈只允许可序列化的数据:" + i.constructor.name);
        //     }
        // }
        data = JSON.parse(JSON.stringify(data));
        
        //判断this.isOnException判断添加位置
        if(this.isOnException){
            this.exceptionTempStack.push(data);
        }else{
            
            this.stack.push(data);
        }
    }

    //** Public */
    push(intent){//push相比较add，push更宽松，没有数据检查，但更可能引发错误
        if(intent == undefined || intent.constructor.name != "Array"){
            throw TypeError('[Intent] 错误的intent数据');
        }

        //判断this.isOnException判断添加位置
        if(this.isOnException){
            this.exceptionTempStack.push(intent);
        }else{
            this.stack.push(intent);
        }
    }

    setIsOnException(is){//设置是否在异常处理状态，异常处理状态下 add 的行为逻辑是不同的，直接影响到intent栈
        this.isOnException = is;
        if(is == false){
            //将ExceptionTempStack的状态栈附加到主栈
            while(this.exceptionTempStack.length > 0){
                this.stack.unshift(this.exceptionTempStack.pop());
            }
        }
    }

    getHandler(){//根据当前的状态栈获取当前的异常处理器

        let name = undefined;
        if(this.handlerStack.length > 0){
            name = this.handlerStack[this.handlerStack.length-1];
        }

        if(name != undefined && name.constructor.name == "String"){
            let fun;
            if(this.exceptionStackHandlerFinder != undefined){
                fun = this.exceptionStackHandlerFinder(name);
                if(fun.constructor.name == "Function"){
                    return fun;
                }
            }
        }

        if(this.exceptionHandler != undefined){
            return this.exceptionHandler;
        }

        throw ReferenceError("[Intent] 无法找到异常处理器\n解决办法：使用setExceptionHandler设置固定的处理器或者使用setExceptionHandlerFinder设置更加灵活的处理器");
    }

    getCheck(){
        let name = undefined;
        if(this.handlerStack.length > 0){
            name = this.handlerStack[this.handlerStack.length-1];
        }

        if(name != undefined && name.constructor.name == "String"){
            let fun;
            if(this.checkStackHandlerFinder != undefined){
                fun = this.checkStackHandlerFinder(name);
                if(fun.constructor.name == "Function"){
                    return fun;
                }
            }
        }

        if(this.checkHandler != undefined){
            return this.checkHandler;
        }

        throw ReferenceError("[INTENT] 无法找到检查处理器");
    }

    getPack(name){//获取栈包
        if(name == undefined || this.intentPackFinder == undefined){
            throw ReferenceError("[INTENT] 无法找到 IntentPack");
        }
        
        let result =  this.intentPackFinder(name);
        if(result.constructor.name != "Function"){
            throw ReferenceError(`[INTENT] 错误的IntentPack，${name} 应为一个 Function`);
        }

        return result;
    }

    
    //** Public */
    next(){

        //读取下一个数据
        let intent = this.stack.shift();
        if(intent == undefined){
            //报INTEND_FINISHED，使用默认或设置的Handler处理
            this.setIsOnException(true);
            this.exceptionReturnHandler(
                this.getHandler().call(
                    this.applyObj,
                    new Exception(
                        this.applyObj,
                        [],
                        INTENT_FINISHED,
                        this.getStackTrace()
                    )
                ),intent
            );
            return;
        }

        let next = [].concat(intent);

        //获取操作函数名
        let fn = next.shift();
        //判断Intent的内置方法
        if(fn == INTENT_STOP){//判断是否是INTENT_STOP
            let tick = next.shift();
            if(tick <= 1){
                return;
            }
            this.stack.unshift([INTENT_STOP,--tick]);
            return;
        }else if(fn == INTENT_THROW){//判断是否是自定义抛出异常
            //获取异常名称
            let exceptionName = next.shift();
            this.setIsOnException(true);
            this.exceptionReturnHandler(
                this.getHandler().call(
                    this.applyObj,
                    new Exception(
                        this.applyObj,
                        intent,
                        exceptionName,
                        this.getStackTrace()
                    )
                ),intent
            );
            return;
        }else if(fn == INTENT_IN){//handler压栈
            let name = next.shift();
            this.handlerStack.push(name);

            this.nextCallStackDeep++;
            this.next();
            this.nextCallStackDeep--;

            return;
        }else if(fn == INTENT_OUT){//出栈
            //报INTEND_FINISHED，使用设置的Handler处理
            this.setIsOnException(true);
            this.exceptionReturnHandler(
                this.getHandler().call(
                    this.applyObj,
                    new Exception(
                        this.applyObj,
                        [],
                        INTENT_FINISHED,
                        this.getStackTrace()
                    )
                ),intent
            );
            this.handlerStack.pop();

            this.nextCallStackDeep++;
            this.next();
            this.nextCallStackDeep--;
            return;
        }else if(fn == INTENT_PACK){//解包
            let packname = next.shift();
            let data = next.shift();
            let pack = this.getPack(packname);
            this.setIsOnException(true);
            pack.apply(this.applyObj,data);
            this.setIsOnException(false);

            this.nextCallStackDeep++;
            this.next();
            this.nextCallStackDeep--;
            return;
        }else if(fn == INTENT_SYNC){
            let num = next.shift();
            for(let i = 0 ; i < num ; i++){
                this.nextCallStackDeep++;
                this.next();
                this.nextCallStackDeep--;
            }
            return;
        }else if(fn == INTENT_CHECK){
            this.setIsOnException(true);
            let data = next.shift();
            let result = this.getCheck().apply(
                this.applyObj,
                data,
            );
            this.setIsOnException(false);
            if(result == INTENT_RETURN_RELOAD){
                this.nextCallStackDeep++;
                this.next();
                this.nextCallStackDeep--;
            }else if(result == INTENT_RETURN_FAIL){
                this.clear();
            }
            return;
        }


        if(this.controlObj == undefined){
            this.setIsOnException(true);
            this.exceptionReturnHandler(
                this.getHandler().call(
                        this.applyObj,
                        new Exception(
                            this.applyObj,
                            intent,
                            INTENT_NO_CONTROL_OBJ,
                            this.getStackTrace()
                    )
                ),
                intent
            );
            return;
        }
        if(this.controlObj[fn] == undefined || this.controlObj[fn].constructor.name != "Function"){
            this.setIsOnException(true);
            this.exceptionReturnHandler(
                this.getHandler().call(
                    this.applyObj,
                    new Exception(
                        this.applyObj,
                        intent,
                        INTENT_NO_CONTROL_OBJ_FUNCTION,
                        this.getStackTrace()
                    )
                ),
                intent
            );
            return;
        }
        let result;
        try{
            //对参数next转化，将字段转换为Object
            next = next.map(function(i){
                if(i.constructor.name == "Object"){
                    if(i.type == "IntentObject"){
                        return Game.getObjectById(i.id);
                    }
                }
                return i;
            });
            result = this.controlObj[fn].apply(this.controlObj,next);
        }catch(e){
            console.log("[Intent]内部错误:");
            console.log(e);
            this.setIsOnException(true);
            this.exceptionReturnHandler(
                this.getHandler().call(
                    this.applyObj,
                    new Exception(
                        this.applyObj,
                        intent,
                        INTENT_CANT_FIND_OBJ,
                        this.getStackTrace()
                    )
                ),intent);
            return;
        }
        if(result != OK){//触发异常处理
            this.setIsOnException(true);
            this.exceptionReturnHandler(
                this.getHandler().call(
                    this.applyObj,
                    new Exception(
                        this.applyObj,
                        intent,
                        result,
                        this.getStackTrace()
                    )
                ),
                intent
            );
        }else{//触发检查处理
            
            this.getCheck().call(this.applyObj,intent,this.getStackTrace());
        }
    }

    exceptionReturnHandler(result = INTENT_RETURN_DEFAULT,intent){

        this.setIsOnException(false);

        if(result == INTENT_RETURN_DEFAULT || result == INTENT_RETURN_RELOAD){            
            if(result == INTENT_RETURN_RELOAD){
                this.nextCallStackDeep++;
                this.next();
                this.nextCallStackDeep--;
            }
        }else if(result == INTENT_RETURN_FAIL){
            this.clear();
        }else{
            throw ReferenceError("[INTENT] 无效的返回值");
        }
    }

    clear(){
        this.stack = []; 
        this.handlerStack = [];
        this.isOnException = false;
        this.exceptionTempStack = [];
    }

    getStackTrace(){
        let temp = "";
        for (let i= this.handlerStack.length-1;i >= 0;i--){
            temp += `at ${this.handlerStack[i]}\n`
        }
        return temp;
    }
}


global.IntentStack = IntentStack;
global.IntentObject = IntentObject;
